﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Reflection;
using System.Text;

namespace OperationPlugins.Proxies.CodeDom.CustomAttributeTypedArgumentValueParsers
{
    /// <summary>
    /// Parses String values from CustomAttributeTypedArgument objects.
    /// </summary>
    [Export(typeof(ICustomAttributeTypedArgumentValueParser))]
    internal sealed class StringParser : ICustomAttributeTypedArgumentValueParser
    {
        bool ICustomAttributeTypedArgumentValueParser.TryParse(System.Reflection.CustomAttributeTypedArgument arg, out string value)
        {
            value = null;

            if (arg.ArgumentType == typeof(String[]))
            {
                var items = (ReadOnlyCollection<CustomAttributeTypedArgument>)arg.Value;

                value = string.Format("new System.String[{0}] {{ ", items.Count);

                for (int i = 0; i < items.Count; i++)
                {
                    var item = (String)items[i].Value;
                    var v = Parse(item);

                    if (i == 0)
                    {
                        value = string.Format("{0} {1}", value, v);
                    }
                    else
                    {
                        value = string.Format("{0}, {1}", value, v);
                    }
                }

                value = string.Format("{0} }}", value);

                return true;
            }

            if (arg.ArgumentType == typeof(String))
            {
                value = Parse((String)arg.Value);
                return true;
            }

            return false;
        }

        private static string Parse(String v)
        {
            string value = null;

            if (v != null)
            {
                string literal = ToLiteral(v);

                value = string.Format("{0}", literal);
            }

            return value;
        }

        private static string ToLiteral(string input)
        {
            var literal = new StringBuilder(input.Length + 2);
            literal.Append("\"");
            foreach (var c in input)
            {
                switch (c)
                {
                    case '\'': literal.Append(@"\'"); break;
                    case '\"': literal.Append("\\\""); break;
                    case '\\': literal.Append(@"\\"); break;
                    case '\0': literal.Append(@"\0"); break;
                    case '\a': literal.Append(@"\a"); break;
                    case '\b': literal.Append(@"\b"); break;
                    case '\f': literal.Append(@"\f"); break;
                    case '\n': literal.Append(@"\n"); break;
                    case '\r': literal.Append(@"\r"); break;
                    case '\t': literal.Append(@"\t"); break;
                    case '\v': literal.Append(@"\v"); break;
                    default:
                        if (Char.GetUnicodeCategory(c) != UnicodeCategory.Control)
                        {
                            literal.Append(c);
                        }
                        else
                        {
                            literal.Append(@"\u");
                            literal.Append(((ushort)c).ToString("x4"));
                        }
                        break;
                }
            }
            literal.Append("\"");
            return literal.ToString();
        }

    }
}
